package com.ssau.expert.core.impl;

import com.intelli.ray.core.Inject;
import com.intelli.ray.core.ManagedComponent;
import com.ssau.expert.app.DatabaseAPI;
import com.ssau.expert.common.*;
import com.ssau.expert.core.api.DatabaseCoreAPI;
import com.ssau.expert.core.api.ExpertCoreAPI;
import com.ssau.expert.core.model.FuzzyRule;
import com.ssau.expert.core.model.Rule;
import com.ssau.expert.core.util.RuleDTOParser;
import com.ssau.expert.exception.NotInitializedException;
import com.ssau.expert.exception.RuleParseException;
import com.ssau.expert.exception.SemanticException;
import com.ssau.expert.util.FileIO;
import com.ssau.expert.util.Names;
import com.ssau.expert.util.QuestionHelper;

import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.List;

/**
 * Author: Sergey Saiyan
 * Created: 05.09.13 12:04
 */
@ManagedComponent(name = "ExpertSystem")
public class ExpertSystem implements ExpertCoreAPI {

    @Inject
    private DatabaseCoreAPI database;
    @Inject
    protected RuleParser ruleParser;
    @Inject
    protected SuggestHelper suggestHelper;

    private State state = State.NOT_INITIALIZED;
    private String algebra = "AM";

    protected QuestionRequestListener listener = null;

    public static final int DEFAULT_UNDEFINED_BARRIER = 50;
    private int undefinedBarrier = DEFAULT_UNDEFINED_BARRIER;

    public String getAlgebra() {
        return algebra;
    }

    public void setAlgebra(String algebra) {
        this.algebra = algebra;
    }

    @Override
    public void uploadFileWithRules(File file) throws IOException, RuleParseException, SemanticException {
        ruleParser.uploadRules(file);
    }

    @Override
    public void initExpertSystem() throws NotInitializedException {
        if (database.getRules().isEmpty()) {
            throw new NotInitializedException("Rules were not initialized");
        }

        try {
            QuestionHelper.initQuestions();
        } catch (IOException e) {
            throw new NotInitializedException("Can't init questions. Check template paths.");
        }

        state = State.INITIALIZED;
    }

    @Override
    public void setQuestionRequestListener(QuestionRequestListener listener) {
        this.listener = listener;
    }

    @Override
    public QuestionRequestListener getListener() {
        return listener;
    }

    @Override
    public SuggestedResult getResults() {
        return suggestHelper.doSuggestion();
    }

    @Override
    public DatabaseAPI getDatabase() {
        return database;
    }

    @Override
    public List<String> getLog() {
        return Logger.get().getLog();
    }

    @Override
    public Answer getShipProfileAnswer() {
        Answer value = suggestHelper.table.getVariable(Names.PROFILE).getAnswer();
        return value != null ? value : null;
    }

    @Override
    public Answer getStaffAnswer() {
        Answer value = suggestHelper.table.getVariable(Names.STUFF).getAnswer();
        return value != null ? value : null;
    }

    @Override
    public Answer getDistanceAnswer() {
        Answer value = suggestHelper.table.getVariable(Names.DISTANCE).getAnswer();
        return value != null ? value : null;
    }

    @Override
    public void setState(State state) {
        this.state = state;
    }

    @Override
    public State getState() {
        return state;
    }

    @Override
    public int getUndefinedBarrier() {
        if (undefinedBarrier == DEFAULT_UNDEFINED_BARRIER) {
            Integer barrier = FileIO.readUndefinedBarrier();
            if (barrier != null) {
                undefinedBarrier = barrier;
            }
        }

        return undefinedBarrier;
    }

    @Override
    public void setUndefinedBarrier(int undefinedBarrier) {
        this.undefinedBarrier = undefinedBarrier;
        FileIO.setUndefinedBarrier(undefinedBarrier);
    }

    @Override
    public CallableTask saveExpertCoeffs(List<RuleDTO> ruleDTOList) {

        Collection<Rule> backupRules = database.getRuleCollection();
        database.clearRules();

        for (RuleDTO ruleDTO : ruleDTOList) {
            FuzzyRule[] rules = RuleDTOParser.parse(ruleDTO);

            database.putRule(rules[0]);
        }

        FileIO.saveCoeffs(ruleDTOList);

        return new CallableTask() {
            @Override
            public void execute() {

            }
        };
    }

    public List<RuleDTO> getExpertCoeffs() {
        return FileIO.getCoeffs();
    }

    public enum State {
        INITIALIZED,
        NOT_INITIALIZED,
        WAITING_INPUT,
        FINISHED
    }
}
